Skip to content

Conversation

@getvictor
Copy link
Member

@getvictor getvictor commented Oct 23, 2025

Related issue: Resolves #34542

  • Added SCEP endpoint for issuing certs for conditional access for Okta. Functionally similar to host identity and Apple MDM SCEP endpoints.
  • Changes file will be added later (this is a sub-task of the feature).
  • A standard SCEP payload can be used to get a cert to an Apple device:
<!-- SCEP Configuration -->
<dict>
	<key>PayloadContent</key>
	<dict>
		<key>URL</key>
		<string>https://myfleet.example.com/api/fleet/conditional_access/scep</string>
		<key>Challenge</key>
		<string>ENROLLMENT_SECRET</string>
		<key>Keysize</key>
		<integer>2048</integer>
		<key>Key Type</key>
		<string>RSA</string>
		<key>Key Usage</key>
		<integer>5</integer>
              <key>ExtendedKeyUsage</key>
              <array>
                  <string>1.3.6.1.5.5.7.3.2</string>
              </array>
		<key>Subject</key>
		<array>
			<array>
				<array>
					<string>CN</string>
					<string>Fleet conditional access for Okta</string>
				</array>
			</array>
		</array>
		<key>SubjectAltName</key>
		<dict>
			<key>uniformResourceIdentifier</key>
			<array>
				<string>urn:device:apple:uuid:%HardwareUUID%</string>
			</array>
		</dict>
		<key>Retries</key>
		<integer>3</integer>
		<key>RetryDelay</key>
		<integer>10</integer>
              <!-- ACL for browser access -->
              <key>AllowAllAppsAccess</key>
              <true/>
              <!-- Set true for Safari access. Set false if Safari support not needed. -->
              <key>KeyIsExtractable</key>
              <false/>
	</dict>
	<key>PayloadDescription</key>
	<string>Configures SCEP for Fleet conditional access for Okta certificate</string>
	<key>PayloadDisplayName</key>
	<string>Fleet conditional access SCEP</string>
	<key>PayloadIdentifier</key>
	<string>com.fleetdm.conditional-access-scep</string>
	<key>PayloadType</key>
	<string>com.apple.security.scep</string>
	<key>PayloadUUID</key>
	<string>B2C3D4E5-F6A7-4B6C-9D8E-0F1A2B3C4D5E</string>
	<key>PayloadVersion</key>
	<integer>1</integer>
</dict>

Checklist for submitter

Testing

  • Added/updated automated tests
  • QA'd all new/changed functionality manually

Database migrations

  • Ensured the correct collation is explicitly set for character columns (COLLATE utf8mb4_unicode_ci).

Summary by CodeRabbit

New Features

  • Adds Conditional Access SCEP certificate enrollment support, enabling hosts to obtain device identity certificates through secure certificate enrollment protocol endpoints.
  • Implements rate limiting for certificate enrollment requests to prevent abuse.

Tests

  • Adds comprehensive integration tests for Conditional Access SCEP functionality, including certificate operations, rate limiting validation, and edge cases.

@codecov
Copy link

codecov bot commented Oct 23, 2025

Codecov Report

❌ Patch coverage is 77.44108% with 67 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.16%. Comparing base (8f3888f) to head (742d83d).
⚠️ Report is 10 commits behind head on main.

Files with missing lines Patch % Lines
ee/server/service/condaccess/scep.go 67.05% 16 Missing and 12 partials ⚠️
ee/server/service/condaccess/depot/depot.go 77.21% 10 Missing and 8 partials ⚠️
ee/server/service/condaccess/config.go 79.31% 3 Missing and 3 partials ⚠️
...s/20251106000000_AddConditionalAccessSCEPTables.go 82.35% 4 Missing and 2 partials ⚠️
cmd/fleet/serve.go 0.00% 5 Missing ⚠️
ee/server/integrationtest/condaccess/suite.go 93.93% 2 Missing ⚠️
server/datastore/mysql/conditional_access_scep.go 92.85% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #34721      +/-   ##
==========================================
+ Coverage   66.09%   66.16%   +0.06%     
==========================================
  Files        2080     2085       +5     
  Lines      176330   176367      +37     
  Branches     7166     7137      -29     
==========================================
+ Hits       116554   116689     +135     
+ Misses      49069    48947     -122     
- Partials    10707    10731      +24     
Flag Coverage Δ
backend 67.76% <77.44%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

# Conflicts:
#	server/datastore/mysql/hosts.go
#	server/datastore/mysql/hosts_test.go
#	server/datastore/mysql/schema.sql
@getvictor
Copy link
Member Author

@coderabbitai full review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 3, 2025

✅ Actions performed

Full review triggered.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 3, 2025

Walkthrough

This PR introduces Conditional Access SCEP certificate enrollment support for Fleet, enabling secure certificate issuance for third-party MDM integrations. The implementation includes a new SCEP service, MySQL-backed certificate depot, database schema for tracking certificates and serials, and comprehensive integration tests with rate limiting and enrollment secret verification.

Changes

Cohort / File(s) Summary
SCEP Service & Configuration
cmd/fleet/serve.go, ee/server/service/condaccess/scep.go, ee/server/service/condaccess/config.go
Registers Conditional Access SCEP endpoint at /api/fleet/conditional_access/scep, initializes CA certificate/key assets on startup, implements SCEP service with GetCACaps, GetCACert, and PKIOperation handlers; includes enrollment secret challenge verification and rate limit error handling via RateLimitError type.
Certificate Depot
ee/server/service/condaccess/depot/depot.go
Implements MySQL-backed Depot for certificate lifecycle management; CA() retrieves stored certificates, Serial() generates incremental serials, Put() validates CSRs, extracts device UUID from SAN URIs, enforces rate limiting via enroll cooldown, and persists certificates with grace period before revocation support.
Database Layer - Readers
server/datastore/mysql/conditional_access_scep.go
Adds GetConditionalAccessCertHostIDBySerialNumber() and GetConditionalAccessCertCreatedAtByHostID() methods to query non-revoked, non-expired certificates and track enrollment timestamps for rate-limit enforcement.
Database Layer - Schema & Migration
server/datastore/mysql/migrations/tables/20251103000000_AddConditionalAccessSCEPTables.go, server/datastore/mysql/schema.sql
Defines two MySQL tables: conditional_access_scep_serials (auto-increment PK) and conditional_access_scep_certificates (serial PK, host_id, name, validity window, certificate_pem, revocation status); includes CHECK constraint and foreign key relationship.
Database Integration
server/datastore/mysql/mysql.go, server/datastore/mysql/hosts.go
Adds datastore factory method NewConditionalAccessSCEPDepot(); includes conditional_access_scep_certificates in hostRefs for cascade deletion when hosts are removed.
Interface & Constants
server/fleet/datastore.go, server/fleet/mdm.go
Extends Datastore interface with conditional access certificate query methods; defines MDMAssetConditionalAccessCACert and MDMAssetConditionalAccessCAKey constants.
Mock & Test Infrastructure
server/mock/datastore_mock.go, server/service/testing_utils.go
Adds mock implementations of certificate query methods following existing patterns; extends TestServerOpts with ConditionalAccess field to configure SCEP storage in test server setup.
Integration Tests
ee/server/integrationtest/condaccess/suite.go, ee/server/integrationtest/condaccess/condaccess_test.go, ee/server/integrationtest/condaccess/scep_rate_limit_test.go
Provides test suite setup (SetUpSuite, SetUpSuiteWithConfig) with MySQL environment and admin token provisioning; tests SCEP enrollment flow (GetCACaps, GetCACert, CSR signing), invalid challenge/UUID scenarios, and rate limit behavior (429 response on duplicate enrollment within cooldown).
Unit Tests
ee/server/service/condaccess/config_test.go, server/datastore/mysql/conditional_access_scep_test.go, server/datastore/mysql/hosts_test.go
Tests asset initialization idempotency, CA certificate properties (10-year validity, RSA-2048), certificate query methods filtering by revocation/expiration, and host deletion cascade to conditional access certificates.

Sequence Diagram

sequenceDiagram
    participant Client
    participant SCEP as SCEP Handler
    participant Middleware as Challenge Middleware
    participant Depot
    participant DB

    Client->>SCEP: GetCACaps()
    SCEP->>DB: Retrieve CA cert/key
    SCEP-->>Client: SHA-256, AES, POSTPKIOperation<br/>(renewal not supported)

    Client->>SCEP: GetCACert()
    SCEP->>DB: Retrieve CA certificate
    SCEP-->>Client: CA certificate (DER)

    Client->>SCEP: PKIOperation(CSR with challenge)
    Note over SCEP: Decrypt SCEP envelope
    SCEP->>Middleware: challengeMiddleware validates<br/>enrollment secret
    alt Challenge Valid
        Middleware->>Depot: Sign CSR
        Depot->>Depot: Extract UUID from SAN URI
        Depot->>DB: Check host exists
        Depot->>DB: Check rate limit<br/>(enroll cooldown)
        alt Rate Limit Exceeded
            Depot-->>SCEP: RateLimitError
            SCEP-->>Client: HTTP 429
        else Rate Limit OK
            Depot->>DB: Insert certificate<br/>+ serial
            Depot-->>SCEP: Signed certificate
            SCEP-->>Client: Certificate (SCEP envelope)
        end
    else Challenge Invalid / Missing UUID
        Middleware-->>SCEP: PKI Failure
        SCEP-->>Client: PKI Failure response<br/>(no certificate issued)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Areas requiring extra attention:

  • ee/server/service/condaccess/depot/depot.go: Contains rate limiting logic, SAN URI parsing for device UUID extraction, and certificate lifecycle management with grace period handling—verify correctness of CSR validation, UUID parsing, and enrollment cooldown enforcement.
  • ee/server/service/condaccess/scep.go: Implements core SCEP service with PKI operation handling and error mapping to HTTP status codes—ensure challenge middleware correctly validates enrollment secrets and RateLimitError is properly surfaced.
  • Database schema & migration (server/datastore/mysql/migrations/tables/20251103000000_AddConditionalAccessSCEPTables.go, server/datastore/mysql/schema.sql): New foreign key and CHECK constraints; verify migration is backward-compatible and forward-compatible with future schema changes.
  • ee/server/integrationtest/condaccess/condaccess_test.go: Large test suite with extensive mocking of CA interactions, CSR generation, and response parsing—validate test scenarios cover edge cases (invalid challenge, missing UUID, certificate expiry filtering).
  • Rate limiting test (ee/server/integrationtest/condaccess/scep_rate_limit_test.go): Confirm that cooldown timing and HTTP 429 status code are correctly enforced across different hosts.

Possibly related PRs

  • Add SCEP endpoint for host identity. #30589: Adds Host Identity SCEP support with analogous changes to cmd/fleet/serve.go for SCEP depot creation/registration and similar database schema pattern; establishes precedent for SCEP implementation in Fleet.

Suggested labels

conditional-access, scep, database, integration-test, certificate-management

Suggested reviewers

  • lucasmrod
  • lukeheath
  • dantecatalfamo

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title check ⚠️ Warning The title "Okta SCEP endpoint" is vague and misleading as the changes implement a generic Conditional Access SCEP endpoint, not specifically an Okta SCEP endpoint. Revise the title to "Add Conditional Access SCEP endpoint" or similar to accurately reflect the implementation scope.
Description check ⚠️ Warning The PR description covers the main objective and includes a SCEP configuration example, but is missing several required checklist items. Complete the missing checklist items: add Changes file entry, verify SQL injection prevention and schema updates, confirm endpoint backward compatibility, and ensure all database migration checks are addressed.
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed All coding requirements from #34542 are implemented: Conditional Access SCEP endpoint with enroll secret challenge, new database tables, and similar functionality to existing SCEP endpoints.
Out of Scope Changes check ✅ Passed All changes are in scope: SCEP endpoint implementation, depot interface, database migrations, datastore methods, and comprehensive integration tests for Conditional Access SCEP functionality.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch victor/34542-okta-scep

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (5)
server/datastore/mysql/hosts_test.go (1)

8479-8489: Cover cleanup verification for conditional_access_scep_certificates.

Good insertion to exercise CA SCEP, but if conditional_access_scep_certificates isn’t included in hostRefs, the test won’t assert it’s removed by DeleteHosts. Add the table to hostRefs, or add targeted pre/post asserts here to guarantee coverage.

Suggested minimal assertions:

@@
   _, err = ds.writer(context.Background()).Exec(`
       INSERT INTO conditional_access_scep_certificates (serial, host_id, name, not_valid_before, not_valid_after, certificate_pem, revoked)
       VALUES (?, ?, ?, ?, ?, ?, ?)
     `, caCertSerial, host.ID, "test-ca-host", time.Now().Add(-1*time.Hour), time.Now().Add(24*time.Hour), "-----BEGIN CERTIFICATE-----", false)
   require.NoError(t, err)
+
+  // Assert CA SCEP row exists pre-delete
+  var caCount int
+  err = ds.writer(context.Background()).Get(&caCount, `SELECT COUNT(1) FROM conditional_access_scep_certificates WHERE host_id = ?`, host.ID)
+  require.NoError(t, err)
+  require.Equal(t, 1, caCount)
@@
   err = ds.DeleteHosts(context.Background(), []uint{host.ID})
   require.NoError(t, err)
@@
   // Check that all the associated tables were cleaned up.
+  // Assert CA SCEP row removed post-delete
+  err = ds.writer(context.Background()).Get(&caCount, `SELECT COUNT(1) FROM conditional_access_scep_certificates WHERE host_id = ?`, host.ID)
+  require.True(t, err == nil || errors.Is(err, sql.ErrNoRows))
+  require.Zero(t, caCount)

Also, please verify conditional_access_scep_certificates is listed in hostRefs so it’s covered by the generic loops. If it is, the direct asserts are optional but still helpful as a focused guard.

ee/server/integrationtest/condaccess/scep_rate_limit_test.go (2)

27-29: Consider adding hosts table cleanup.

The deferred cleanup only truncates the conditional_access_scep_* tables, but the test creates hosts (lines 39-47, 61-69) that may not be cleaned up. If the BaseSuite doesn't automatically clean the hosts table, consider adding it to the truncation list to prevent test data leakage.

 	defer mysql.TruncateTables(t, s.BaseSuite.DS, []string{
-		"conditional_access_scep_serials", "conditional_access_scep_certificates",
+		"conditional_access_scep_serials", "conditional_access_scep_certificates", "hosts",
 	}...)

20-74: Consider testing cooldown expiration.

The test configures a 5-minute cooldown (line 22) and verifies immediate requests are rate-limited, but doesn't verify that requests succeed after the cooldown period expires. Consider adding a test case that:

  1. Manipulates the certificate's created_at timestamp to simulate cooldown expiration
  2. Verifies the same host can successfully request a new certificate after cooldown

This would provide more comprehensive coverage of the rate-limiting behavior.

server/datastore/mysql/conditional_access_scep_test.go (1)

189-193: Tighten the negative-path assertion

Right now the missing-UUID case only probes serial 1 to assert nothing was persisted. That will miss a regression if the table’s auto-increment ever skips over 1 (e.g. when TRUNCATE is not used) and we mistakenly store a certificate under serial 2+. It’d be safer to assert by host—for example, call GetConditionalAccessCertCreatedAtByHostID for the host under test and expect a not-found error—so we fail regardless of the next serial value.

server/datastore/mysql/conditional_access_scep.go (1)

16-20: Consider adding LIMIT 1 for defensive coding and explicit intent.

While certificate serial numbers should be unique and sqlx.GetContext will error on multiple rows, adding LIMIT 1 makes the uniqueness expectation explicit and guards against potential schema issues or bugs.

Apply this diff:

 	stmt := `
 		SELECT host_id
 		FROM conditional_access_scep_certificates
 		WHERE serial = ? AND revoked = 0 AND not_valid_after > NOW()
+		LIMIT 1
 	`
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 59a73b1 and a714c33.

📒 Files selected for processing (19)
  • cmd/fleet/serve.go (2 hunks)
  • ee/server/integrationtest/condaccess/condaccess_test.go (1 hunks)
  • ee/server/integrationtest/condaccess/scep_rate_limit_test.go (1 hunks)
  • ee/server/integrationtest/condaccess/suite.go (1 hunks)
  • ee/server/service/condaccess/config.go (1 hunks)
  • ee/server/service/condaccess/config_test.go (1 hunks)
  • ee/server/service/condaccess/depot/depot.go (1 hunks)
  • ee/server/service/condaccess/scep.go (1 hunks)
  • server/datastore/mysql/conditional_access_scep.go (1 hunks)
  • server/datastore/mysql/conditional_access_scep_test.go (1 hunks)
  • server/datastore/mysql/hosts.go (1 hunks)
  • server/datastore/mysql/hosts_test.go (1 hunks)
  • server/datastore/mysql/migrations/tables/20251103000000_AddConditionalAccessSCEPTables.go (1 hunks)
  • server/datastore/mysql/mysql.go (2 hunks)
  • server/datastore/mysql/schema.sql (2 hunks)
  • server/fleet/datastore.go (1 hunks)
  • server/fleet/mdm.go (1 hunks)
  • server/mock/datastore_mock.go (3 hunks)
  • server/service/testing_utils.go (4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

⚙️ CodeRabbit configuration file

When reviewing SQL queries that are added or modified, ensure that appropriate filtering criteria are applied—especially when a query is intended to return data for a specific entity (e.g., a single host). Check for missing WHERE clauses or incorrect filtering that could lead to incorrect or non-deterministic results (e.g., returning the first row instead of the correct one). Flag any queries that may return unintended results due to lack of precise scoping.

Files:

  • cmd/fleet/serve.go
  • ee/server/integrationtest/condaccess/condaccess_test.go
  • server/fleet/mdm.go
  • server/datastore/mysql/conditional_access_scep_test.go
  • server/datastore/mysql/mysql.go
  • server/datastore/mysql/hosts.go
  • ee/server/integrationtest/condaccess/suite.go
  • ee/server/service/condaccess/config_test.go
  • ee/server/service/condaccess/depot/depot.go
  • server/datastore/mysql/conditional_access_scep.go
  • server/fleet/datastore.go
  • ee/server/integrationtest/condaccess/scep_rate_limit_test.go
  • server/mock/datastore_mock.go
  • ee/server/service/condaccess/scep.go
  • server/datastore/mysql/hosts_test.go
  • server/datastore/mysql/migrations/tables/20251103000000_AddConditionalAccessSCEPTables.go
  • server/service/testing_utils.go
  • ee/server/service/condaccess/config.go
🧠 Learnings (12)
📓 Common learnings
Learnt from: getvictor
Repo: fleetdm/fleet PR: 34566
File: server/service/integration_core_test.go:7500-7511
Timestamp: 2025-10-21T16:04:18.069Z
Learning: Okta conditional access app config in Fleet is Premium-gated and supported both on-prem and in Fleet Cloud; the Cloud-only enforcement applies to the Microsoft compliance partner endpoints, not to the Okta settings.
📚 Learning: 2025-07-08T16:06:54.576Z
Learnt from: getvictor
Repo: fleetdm/fleet PR: 30589
File: ee/server/service/hostidentity/depot/depot.go:104-119
Timestamp: 2025-07-08T16:06:54.576Z
Learning: In ee/server/service/hostidentity/depot/depot.go, the security concern where shared challenges allow certificate revocation (lines 104-119) is a known issue that will be addressed in a later feature, not an immediate concern to fix.

Applied to files:

  • cmd/fleet/serve.go
  • ee/server/service/condaccess/depot/depot.go
📚 Learning: 2025-07-08T16:12:48.797Z
Learnt from: getvictor
Repo: fleetdm/fleet PR: 30589
File: ee/server/service/hostidentity/depot/depot.go:108-111
Timestamp: 2025-07-08T16:12:48.797Z
Learning: In ee/server/service/hostidentity/depot/depot.go, the SCEP depot interface methods like Put() do not accept context parameters, and the common_mysql.WithRetryTxx callback function type TxFn only receives a transaction parameter, not a context. Therefore, using context.Background() in tx.ExecContext calls within the transaction callback is the correct approach.

Applied to files:

  • cmd/fleet/serve.go
  • server/datastore/mysql/mysql.go
  • ee/server/service/condaccess/depot/depot.go
📚 Learning: 2025-10-03T18:16:11.482Z
Learnt from: MagnusHJensen
Repo: fleetdm/fleet PR: 33805
File: server/service/integration_mdm_test.go:1248-1251
Timestamp: 2025-10-03T18:16:11.482Z
Learning: In server/service/integration_mdm_test.go, the helper createAppleMobileHostThenEnrollMDM(platform string) is exclusively for iOS/iPadOS hosts (mobile). Do not flag macOS model/behavior issues based on changes within this helper; macOS provisioning uses different helpers such as createHostThenEnrollMDM.

Applied to files:

  • ee/server/integrationtest/condaccess/condaccess_test.go
  • server/fleet/mdm.go
  • server/datastore/mysql/hosts.go
  • server/datastore/mysql/hosts_test.go
📚 Learning: 2025-08-08T07:40:05.301Z
Learnt from: getvictor
Repo: fleetdm/fleet PR: 31726
File: server/datastore/mysql/labels_test.go:2031-2031
Timestamp: 2025-08-08T07:40:05.301Z
Learning: In fleetdm/fleet repository tests (server/datastore/mysql/labels_test.go and similar), using testing.T.Context() is valid because the project targets a recent Go version where testing.T.Context() exists. Do not suggest replacing t.Context() with context.Background() in this codebase.

Applied to files:

  • server/datastore/mysql/conditional_access_scep_test.go
📚 Learning: 2025-08-08T07:40:05.301Z
Learnt from: getvictor
Repo: fleetdm/fleet PR: 31726
File: server/datastore/mysql/labels_test.go:2031-2031
Timestamp: 2025-08-08T07:40:05.301Z
Learning: Fleet repo targets Go 1.24.5 (root go.mod), which supports testing.T.Context(). Do not flag usage of t.Context() or suggest replacing it with context.Background() in tests (e.g., server/datastore/mysql/labels_test.go Line 2031 and similar).

Applied to files:

  • server/datastore/mysql/mysql.go
📚 Learning: 2025-07-08T16:11:49.555Z
Learnt from: getvictor
Repo: fleetdm/fleet PR: 30589
File: ee/server/service/hostidentity/depot/depot.go:115-115
Timestamp: 2025-07-08T16:11:49.555Z
Learning: In ee/server/service/hostidentity/depot/depot.go, the error from result.RowsAffected() is intentionally ignored because the information is only used for logging purposes, not for critical program logic.

Applied to files:

  • server/datastore/mysql/mysql.go
📚 Learning: 2025-07-07T22:21:15.748Z
Learnt from: getvictor
Repo: fleetdm/fleet PR: 30589
File: server/datastore/mysql/schema.sql:501-517
Timestamp: 2025-07-07T22:21:15.748Z
Learning: In the host_identity_scep_certificates table schema, the VARBINARY(100) size for public_key_raw, the nullable host_id without a foreign key constraint, and the use of plain DATETIME instead of DATETIME(6) are intentional design decisions, not issues to be addressed.

Applied to files:

  • server/datastore/mysql/hosts.go
  • server/datastore/mysql/schema.sql
📚 Learning: 2025-08-08T07:40:05.301Z
Learnt from: getvictor
Repo: fleetdm/fleet PR: 31726
File: server/datastore/mysql/labels_test.go:2031-2031
Timestamp: 2025-08-08T07:40:05.301Z
Learning: In fleetdm/fleet, tests may validly use testing.T.Context() when the module/toolchain targets Go 1.24+. Do not flag t.Context() usage in this codebase if go.mod/toolchain indicates Go >= 1.24.

Applied to files:

  • ee/server/integrationtest/condaccess/suite.go
📚 Learning: 2025-08-01T15:08:16.858Z
Learnt from: sgress454
Repo: fleetdm/fleet PR: 31508
File: server/datastore/mysql/schema.sql:102-116
Timestamp: 2025-08-01T15:08:16.858Z
Learning: The schema.sql file in server/datastore/mysql/ is auto-generated from migrations for use with tests, so it cannot be manually edited. Any changes must be made through migrations.

Applied to files:

  • server/datastore/mysql/schema.sql
📚 Learning: 2025-09-18T21:55:10.359Z
Learnt from: getvictor
Repo: fleetdm/fleet PR: 33184
File: server/datastore/mysql/migrations/tables/20250918154557_AddKernelHostCountsIndexForVulnQueries.go:12-27
Timestamp: 2025-09-18T21:55:10.359Z
Learning: In Fleet's codebase, schema.sql is auto-generated from all existing migrations, so there's no risk of migrations creating duplicate database objects. Migrations don't need to be made idempotent to guard against conflicts with schema.sql since they work together as part of the same system.

Applied to files:

  • server/datastore/mysql/schema.sql
📚 Learning: 2025-07-08T16:13:39.114Z
Learnt from: getvictor
Repo: fleetdm/fleet PR: 30589
File: server/datastore/mysql/migrations/tables/20250707095725_HostIdentitySCEPCertificates.go:53-55
Timestamp: 2025-07-08T16:13:39.114Z
Learning: In the Fleet codebase, Down migration functions are intentionally left empty/no-op. The team does not implement rollback functionality for database migrations, so empty Down_* functions in migration files are correct and should not be flagged as issues.

Applied to files:

  • server/datastore/mysql/migrations/tables/20251103000000_AddConditionalAccessSCEPTables.go
🔇 Additional comments (14)
server/datastore/mysql/hosts.go (1)

533-575: LGTM! Addition follows the established pattern.

The addition of "conditional_access_scep_certificates" to the hostRefs slice is consistent with the existing "host_identity_scep_certificates" entry and correctly ensures that conditional access SCEP certificate records are cleaned up when a host is deleted. The delHostRef function properly filters by host_id IN (?), satisfying the requirement for appropriate filtering criteria.

cmd/fleet/serve.go (1)

1353-1361: LGTM! Conditional Access SCEP registration follows the established pattern.

The implementation correctly mirrors the Host Identity SCEP setup, with proper error handling and appropriate conditional checks for private key availability and premium licensing.

ee/server/service/condaccess/config.go (1)

12-56: LGTM! Asset initialization is well-implemented and idempotent.

The function correctly checks for existing assets before generating new ones, ensuring idempotency. The CA configuration (10-year validity, clear organization name) is appropriate for conditional access certificates.

server/fleet/mdm.go (1)

857-860: LGTM! Asset name constants follow the established naming convention.

The new constants are appropriately named and documented, consistent with existing MDM asset definitions.

server/datastore/mysql/mysql.go (1)

187-191: LGTM! Depot factory method follows the established pattern.

The implementation correctly delegates to the conditional access depot package with appropriate parameters, consistent with the Host Identity SCEP depot factory method.

server/service/testing_utils.go (1)

354-357: LGTM! Test utilities follow the established pattern.

The Conditional Access test support is structured consistently with the existing Host Identity test setup, enabling integration testing of the SCEP flow.

Also applies to: 395-395, 513-515

ee/server/service/condaccess/config_test.go (1)

16-116: LGTM! Comprehensive test coverage for asset initialization.

The test thoroughly validates:

  • Asset creation and retrieval
  • PEM encoding and X.509 certificate parsing
  • Certificate properties (CA flags, key usage, validity period with leap year tolerance)
  • RSA key properties (2048-bit strength)
  • Idempotency (no regeneration on subsequent calls)

The use of t.Context() is appropriate for the Go version in use.

server/datastore/mysql/schema.sql (1)

266-271: Verify datetime precision inconsistency.

The not_valid_before and not_valid_after columns use plain datetime (second precision), while created_at and updated_at use datetime(6) (microsecond precision). This inconsistency within the same table could lead to confusion.

Consider using consistent precision throughout the table. If the validity timestamps don't require microsecond precision, the current design is acceptable, but it would be helpful to document why different precisions are needed.

server/datastore/mysql/conditional_access_scep.go (2)

35-41: Verify rate limiting logic intentionally includes all certificates.

The query returns the most recent certificate's created_at regardless of revoked status or expiration. This likely prevents rapid re-enrollment attempts even after revocation, but please confirm this is the intended rate limiting behavior.


1-51: LGTM: Clean implementation with consistent patterns.

The implementation follows Fleet's datastore patterns well with appropriate use of reader contexts, consistent error handling, and clear separation of concerns for the two lightweight read operations.

server/mock/datastore_mock.go (4)

1574-1577: New CA SCEP mock function types — LGTM

Signatures look right and consistent with existing mocks.


3921-3926: DataStore fields and Invoked flags added — LGTM

Thread-safe flagging matches the file’s established pattern.


9385-9390: GetConditionalAccessCertCreatedAtByHostID mock — LGTM

Matches established mock style; no issues.


9378-9383: Verification passed — all signatures and implementations match

The script output confirms:

  • Interface definitions exist with correct signatures
  • MySQL implementations have matching signatures
  • Mock implementations (lines 9378, 9385) have correct signatures matching the interface
  • Call sites are present and usage is correct

No issues found. The mutex/flag/delegate pattern is correct and consistent as originally noted.

@getvictor getvictor marked this pull request as ready for review November 3, 2025 22:55
@getvictor getvictor requested a review from a team as a code owner November 3, 2025 22:55
// Serial allocates and returns a new (increasing) serial number.
func (d *ConditionalAccessSCEPDepot) Serial() (*big.Int, error) {
// Insert an empty row to generate a new auto-incremented serial number
result, err := d.db.Exec(`INSERT INTO conditional_access_scep_serials () VALUES ();`)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we have other patterns of calling raw sql in the service layer?

Copy link
Member Author

@getvictor getvictor Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

depot.go is not really a service layer, it is a datastore layer, similar to our Apple MDM SCEP

result, err := d.db.Exec(`INSERT INTO scep_serials () VALUES ();`)

Perhaps these files should be under ee/server/condaccess and not under ee/server/service/condaccess?

The condaccess package encapsulates the SCEP logic and the IdP logic for this feature.

I recommend making any directory moves in a subsequent PR.

mostlikelee
mostlikelee previously approved these changes Nov 6, 2025
@getvictor getvictor merged commit 7c9c5b9 into main Nov 6, 2025
34 checks passed
@getvictor getvictor deleted the victor/34542-okta-scep branch November 6, 2025 23:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Okta SCEP endpoint

3 participants